import {
  CallHandler,
  ExecutionContext,
  Injectable,
  Logger,
  NestInterceptor,
} from '@nestjs/common';
import { Reflector } from '@nestjs/core';
import { Observable, tap } from 'rxjs';
import { OperationLogMeta } from '../decorators/log.decorator';
import { Request, Response } from 'express';

import { OperationLogService } from '../../modules/system/log/services/operation-log.service';

@Injectable()
export class LoggingInterceptor implements NestInterceptor {
  constructor(
    private readonly reflector: Reflector,
    private readonly operationLogService: OperationLogService
  ) {}

  // 初始化一个日志类
  logger = new Logger('apilog');

  intercept(context: ExecutionContext, next: CallHandler): Observable<any> {
    const startAt = process.hrtime();
    const request = context.switchToHttp().getRequest<Request>();
    const response = context.switchToHttp().getResponse<Response>();

    const isSse = request.headers.accept === 'text/event-stream';

    // 获取控制器/方法上的日志元数据
    const OperationLogMeta = this.reflector.get<OperationLogMeta>(
      'operationLogMeta',
      context.getHandler()
    );

    // 构建基础日志信息
    const uid = request.user?.uid || null;
    const module = OperationLogMeta?.module || '未知模块';
    const actionType = OperationLogMeta?.actionType || '未知操作类型';

    return next.handle().pipe(
      tap({
        next: responseData => {
          if (isSse) return;
          const duration = this.calculateDuration(startAt);
          this.logger.verbose(`${request.method} ${request.url} ${duration}ms`);
          if (OperationLogMeta) {
            this.operationLogService.create(
              uid,
              module,
              actionType,
              duration
            );
          }
        },
        error: error => {
          this.logger.error('日志拦截出现错误');
        },
      })
    );
  }

  private extractModule(request: Request): string {
    // 从路由路径提取模块名（例如：/api/users => users）
    const pathParts = request.originalUrl.split('?')[0];
    return pathParts ?? '';
  }
  //计算执行时长
  private calculateDuration(startAt: [number, number]): number {
    const [seconds, nanoseconds] = process.hrtime(startAt);
    return Math.round(seconds * 1000 + nanoseconds / 1000000);
  }
}
